package top.gokzzz.log.listener;

import org.springframework.stereotype.Component;
import top.gokzzz.common.utils.BeanUtil;
import top.gokzzz.common.utils.SpringUtil;
import top.gokzzz.log.annotation.DomainLog;
import top.gokzzz.log.dao.SysDomainLogDao;
import top.gokzzz.log.domain.SysDomainLog;

import javax.annotation.PostConstruct;
import javax.persistence.PostPersist;
import javax.persistence.PostRemove;
import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate;
import javax.persistence.Table;
import java.util.UUID;

/**
 * 系統持久化實體審計日誌
 *
 * @author Gok
 * @version 1.0
 */
@Component
public class SysDomainLogListener {

    @PrePersist
    public void onPostPersist(Object entity) {
        logAction(entity, SysDomainLog.Action.INSERT);
    }

    @PreUpdate
    public void onPostUpdate(Object entity) {
        logAction(entity, SysDomainLog.Action.UPDATE);
    }

    @PreRemove
    public void onPostRemove(Object entity) {
        logAction(entity, SysDomainLog.Action.DELETE);
    }

    /**
     * 記入日誌
     *
     * @param entity 實體
     * @param action 操作行為
     */
    private void logAction(Object entity, SysDomainLog.Action action) {
        DomainLog domainLog = entity.getClass().getAnnotation(DomainLog.class);
        if (domainLog == null) {
            return;
        }

        // 獲取表名
        String tableName = getTableName(entity, domainLog);
        String primaryKeyValue = getPrimaryKeyValue(entity, domainLog.primaryKey());
        String dataSnapshot = BeanUtil.bean2Json(entity);

        SysDomainLog sysDomainLog = new SysDomainLog(primaryKeyValue, action, tableName, dataSnapshot);
        sysDomainLog.setId(UUID.randomUUID().toString());
        SysDomainLogDao sysDomainLogDao = SpringUtil.getBean(SysDomainLogDao.class);
        sysDomainLogDao.save(sysDomainLog);
    }

    /**
     * 獲取主鍵
     *
     * @param entity          實體
     * @param primaryKeyField 主鍵屬性欄位
     * @return {@link String }
     */
    private String getPrimaryKeyValue(Object entity, String primaryKeyField) {
        try {
            return (String) entity.getClass().getMethod("get" + capitalize(primaryKeyField)).invoke(entity);
        } catch (Exception e) {
            throw new RuntimeException("無法獲取主鍵值", e);
        }
    }

    /**
     * 獲取表名
     *
     * @param entity    實體
     * @param domainLog 註解
     * @return {@link String } 表名
     */
    private String getTableName(Object entity, DomainLog domainLog) {
        String tableName = domainLog.tableName();
        if (tableName.isEmpty()) {
            // 獲取 @Table 註解
            Table tableAnnotation = entity.getClass().getAnnotation(Table.class);
            if (tableAnnotation != null) {
                // 獲取表名
                tableName = tableAnnotation.name();
            } else {
                // 如果沒有 @Table 註解，則使用類名
                tableName = entity.getClass().getSimpleName();
            }
        }
        return tableName;
    }

    /**
     * 設置第一個字符大寫
     *
     * @param str 字符串
     * @return {@link String }
     */
    private String capitalize(String str) {
        if (str == null || str.isEmpty()) {
            return str;
        }
        return str.substring(0, 1).toUpperCase() + str.substring(1);
    }

}
